// import PropTypes from '../../util';
// import classNames from 'classnames';
import { getNodeChildren, mapChildren, warnOnlyTreeNode } from './util';
import { filterEmpty, getComponentFromProp, initDefaultProps } from '../../util/props-util';
import BaseMixin from '../../util/BaseMixin';

// import getTransitionProps from '../../util/getTransitionProps';

function noop() { }
const ICON_OPEN = 'open';
const ICON_CLOSE = 'close';

const defaultTitle = '---';

const TreeNode = {
  name: 'TreeNode',
  mixins: [BaseMixin],
  __ANT_TREE_NODE: true,
  props: initDefaultProps(
    {
      eventKey: null, // Pass by parent `cloneElement`
      prefixCls: String,
      // className: String,
      root: Object,
      // onSelect: PropTypes.func,

      // By parent
      expanded: Boolean,
      selected: Boolean,
      checked: Boolean,
      loaded: Boolean,
      loading: Boolean,
      halfChecked: Boolean,
      title: null,
      pos: String,
      dragOver: Boolean,
      dragOverGapTop: Boolean,
      dragOverGapBottom: Boolean,

      // By user
      isLeaf: Boolean,
      checkable: Boolean,
      selectable: Boolean,
      disabled: Boolean,
      disableCheckbox: Boolean,
      icon: null,
      dataRef: Object,
      switcherIcon: Function,
      label: null,
      value: null
    },
    {}
  ),

  data() {
    return {
      dragNodeHighlight: false
    };
  },
  inject: {
    vcTree: { default: () => ({}) },
    vcTreeNode: { default: () => ({}) }
  },
  provide() {
    return {
      vcTreeNode: this
    };
  },

  // Isomorphic needn't load data in server side
  mounted() {
    const {
      eventKey,
      vcTree: { registerTreeNode }
    } = this;
    this.syncLoadData(this.$props);
    registerTreeNode && registerTreeNode(eventKey, this);
  },
  updated() {
    this.syncLoadData(this.$props);
  },
  beforeDestroy() {
    const {
      eventKey,
      vcTree: { registerTreeNode }
    } = this;
    registerTreeNode && registerTreeNode(eventKey, null);
  },

  methods: {
    onSelectorClick(e) {
      // Click trigger before select/check operation
      const {
        vcTree: { onNodeClick }
      } = this;
      onNodeClick(e, this);
      if (this.isSelectable()) {
        this.onSelect(e);
      } else {
        this.onCheck(e);
      }
    },

    onSelectorDoubleClick(e) {
      const {
        vcTree: { onNodeDoubleClick }
      } = this;
      onNodeDoubleClick(e, this);
    },

    onSelect(e) {
      if (this.isDisabled()) return;

      const {
        vcTree: { onNodeSelect }
      } = this;
      e.preventDefault();
      onNodeSelect(e, this);
    },

    onCheck(e) {
      if (this.isDisabled()) return;

      const { disableCheckbox, checked } = this;
      const {
        vcTree: { onNodeCheck }
      } = this;

      if (!this.isCheckable() || disableCheckbox) return;

      e.preventDefault();
      const targetChecked = !checked;
      onNodeCheck(e, this, targetChecked);
    },

    onMouseEnter(e) {
      const {
        vcTree: { onNodeMouseEnter }
      } = this;
      onNodeMouseEnter(e, this);
    },

    onMouseLeave(e) {
      const {
        vcTree: { onNodeMouseLeave }
      } = this;
      onNodeMouseLeave(e, this);
    },

    onContextMenu(e) {
      const {
        vcTree: { onNodeContextMenu }
      } = this;
      onNodeContextMenu(e, this);
    },

    onDragStart(e) {
      const {
        vcTree: { onNodeDragStart }
      } = this;

      e.stopPropagation();
      this.setState({
        dragNodeHighlight: true
      });
      onNodeDragStart(e, this);

      try {
        // ie throw error
        // firefox-need-it
        e.dataTransfer.setData('text/plain', '');
      } catch (error) {
        // empty
      }
    },

    onDragEnter(e) {
      const {
        vcTree: { onNodeDragEnter }
      } = this;

      e.preventDefault();
      e.stopPropagation();
      onNodeDragEnter(e, this);
    },

    onDragOver(e) {
      const {
        vcTree: { onNodeDragOver }
      } = this;

      e.preventDefault();
      e.stopPropagation();
      onNodeDragOver(e, this);
    },

    onDragLeave(e) {
      const {
        vcTree: { onNodeDragLeave }
      } = this;

      e.stopPropagation();
      onNodeDragLeave(e, this);
    },

    onDragEnd(e) {
      const {
        vcTree: { onNodeDragEnd }
      } = this;

      e.stopPropagation();
      this.setState({
        dragNodeHighlight: false
      });
      onNodeDragEnd(e, this);
    },

    onDrop(e) {
      const {
        vcTree: { onNodeDrop }
      } = this;

      e.preventDefault();
      e.stopPropagation();
      this.setState({
        dragNodeHighlight: false
      });
      onNodeDrop(e, this);
    },

    // Disabled item still can be switch
    onExpand(e) {
      const {
        vcTree: { onNodeExpand }
      } = this;
      onNodeExpand(e, this);
    },

    getNodeChildren() {
      const {
        $slots: { default: children }
      } = this;
      const originList = filterEmpty(children);
      const targetList = getNodeChildren(originList);

      if (originList.length !== targetList.length) {
        warnOnlyTreeNode();
      }

      return targetList;
    },

    getNodeState() {
      const { expanded } = this;

      if (this.isLeaf2()) {
        return null;
      }

      return expanded ? ICON_OPEN : ICON_CLOSE;
    },

    isLeaf2() {
      const { isLeaf, loaded } = this;
      const {
        vcTree: { loadData }
      } = this;

      const hasChildren = this.getNodeChildren().length !== 0;
      if (isLeaf === false) {
        return false;
      }
      return isLeaf || (!loadData && !hasChildren) || (loadData && loaded && !hasChildren);
    },

    isDisabled() {
      const { disabled } = this;
      const {
        vcTree: { disabled: treeDisabled }
      } = this;

      // Follow the logic of Selectable
      if (disabled === false) {
        return false;
      }

      return !!(treeDisabled || disabled);
    },

    isCheckable() {
      const { checkable } = this.$props;
      const {
        vcTree: { checkable: treeCheckable }
      } = this;

      // Return false if tree or treeNode is not checkable
      if (!treeCheckable || checkable === false) return false;
      return treeCheckable;
    },

    // Load data to avoid default expanded tree without data
    syncLoadData(props) {
      const { expanded, loading, loaded } = props;
      const {
        vcTree: { loadData, onNodeLoad }
      } = this;
      if (loading) return;
      // read from state to avoid loadData at same time
      if (loadData && expanded && !this.isLeaf2()) {
        // We needn't reload data when has children in sync logic
        // It's only needed in node expanded
        const hasChildren = this.getNodeChildren().length !== 0;
        if (!hasChildren && !loaded) {
          onNodeLoad(this);
        }
      }
    },

    isSelectable() {
      const { selectable } = this;
      const {
        vcTree: { selectable: treeSelectable }
      } = this;

      // Ignore when selectable is undefined or null
      if (typeof selectable === 'boolean') {
        return selectable;
      }

      return treeSelectable;
    },

    // Switcher
    renderSwitcher(h) {
      const { expanded } = this;
      const {
        vcTree: { prefixCls }
      } = this;
      const switcherIcon =
        getComponentFromProp(this, 'switcherIcon', {}, false) ||
        getComponentFromProp(this.vcTree, 'switcherIcon', {}, false);
      if (this.isLeaf2()) {
        return h(
          'span',
          {
            class: [`${prefixCls}-switcher`, `${prefixCls}-switcher-noop`],
            dataExpanded: expanded,
            role: 'group'
          },
          [
            typeof switcherIcon === 'function'
              ? switcherIcon({ ...this.$props, ...this.$props.dataRef, isLeaf: true })
              : switcherIcon
          ]
        );
      }

      const switcherCls = `${prefixCls}-switcher ${prefixCls}-switcher_${expanded ? ICON_OPEN : ICON_CLOSE}`
        
      return h(
        'span',
        {
          class: [`${switcherCls}`],
          key: 'switcher',
          on: {
            click: (event) => {
              this.onExpand(event);
            }
          }
        },
        [
          typeof switcherIcon === 'function'
            ? switcherIcon({ ...this.$props, ...this.$props.dataRef, isLeaf: false })
            : switcherIcon
        ]
      );
    },

    // Checkbox
    renderCheckbox(h) {
      const { checked, halfChecked, disableCheckbox } = this;
      const {
        vcTree: { prefixCls }
      } = this;
      const disabled = this.isDisabled();
      const checkable = this.isCheckable();

      if (!checkable) return null;

      // [Legacy] Custom element should be separate with `checkable` in future
      const $custom = typeof checkable !== 'boolean' ? checkable : null;

      return h(
        'span',
        {
          class: [
            `${prefixCls}-checkbox`,
            checked && `${prefixCls}-checkbox-checked`,
            !checked && halfChecked && `${prefixCls}-checkbox-indeterminate`,
            (disabled || disableCheckbox) && `${prefixCls}-checkbox-disabled`
          ],
          on: {
            click: (event) => {
              this.onCheck(event);
            }
          },
          key: 'checkbox'
        },
        [$custom]
      );
    },

    renderIcon(h) {
      const { loading } = this;
      const {
        vcTree: { prefixCls }
      } = this;

      return h('span', {
        class: [
          `${prefixCls}-iconEle`,
          `${prefixCls}-icon__${this.getNodeState() || 'docu'}`,
          loading && `${prefixCls}-icon_loading`
        ],
        key: 'icon'
      });
    },

    // Icon + Title
    renderSelector(h) {
      const { selected, loading, dragNodeHighlight } = this;
      const icon = getComponentFromProp(this, 'icon', {}, false);
      const {
        vcTree: { prefixCls, showIcon, icon: treeIcon, draggable, loadData }
      } = this;
      const disabled = this.isDisabled();
      const title = getComponentFromProp(this, 'title', {}, false);
      const wrapClass = `${prefixCls}-node-content-wrapper`;

      // Icon - Still show loading icon when loading without showIcon
      let $icon;

      if (showIcon) {
        const currentIcon = icon || treeIcon;
        $icon = currentIcon
          ? h(
            'span',
            {
              class: [`${prefixCls}-iconEle`, `${prefixCls}-icon__customize`]
            },
            [
              typeof currentIcon === 'function'
                ? currentIcon({ ...this.$props, ...this.$props.dataRef }, h)
                : currentIcon
            ]
          )
          : this.renderIcon(h);
      } else if (loadData && loading) {
        $icon = this.renderIcon(h);
      }

      const currentTitle = title;
      let $title = currentTitle
        ? h(
          'span',
          {
            class: [`${prefixCls}-title`]
          },
          [
            typeof currentTitle === 'function'
              ? currentTitle({ ...this.$props, ...this.$props.dataRef }, h)
              : currentTitle
          ]
        )
        : h(
          'span',
          {
            class: [`${prefixCls}-title`]
          },
          [defaultTitle]
        );

      return h(
        'span',
        {
          class: [
            `${wrapClass}`,
            `${wrapClass}-${this.getNodeState() || 'normal'}`,
            !disabled && (selected || dragNodeHighlight) && `${prefixCls}-node-selected`,
            !disabled && draggable && 'draggable'
          ],
          key: 'selector',
          ref: 'selectHandle',
          title: typeof title === 'string' ? title : '',
          draggable: (!disabled && draggable) || undefined,
          ariaGrabbed: (!disabled && draggable) || undefined,
          on: {
            mouseenter: (event) => {
              this.onMouseEnter(event);
            },
            mouseleave: (event) => {
              this.onMouseLeave(event);
            },
            contextmenu: (event) => {
              this.onContextMenu(event);
            },
            click: (event) => {
              this.onSelectorClick(event);
            },
            dblclick: (event) => {
              this.onSelectorDoubleClick(event);
            },
            dragstart: (event) => {
              draggable ? this.onDragStart(event) : noop;
            }
          }
        },
        [$icon, $title]
      );
    },

    // Children list wrapped with `Animation`
    renderChildren(h) {
      const { expanded, pos } = this;
      const {
        vcTree: { prefixCls, openTransitionName, openAnimation, renderTreeNode }
      } = this;

      let animProps = {};
      if (openTransitionName) {
        // animProps = getTransitionProps(openTransitionName);
      } else if (typeof openAnimation === 'object') {
        animProps = { ...openAnimation };
        animProps.props = { css: false, ...animProps.props };
      }

      // Children TreeNode
      const nodeList = this.getNodeChildren();

      if (nodeList.length === 0) {
        return null;
      }

      let $children;
      if (expanded) {
        $children = h(
          'ul',
          {
            class: [`${prefixCls}-child-tree`, { [`${prefixCls}-child-tree-open`]: expanded }],
            dataExpanded: expanded,
            role: 'group'
          },
          [mapChildren(nodeList, (node, index) => renderTreeNode(node, index, pos))]
        );
      }
      return h('transition', { ...animProps }, [$children]);
    }
  },

  render(h) {
    const {
      dragOver,
      dragOverGapTop,
      dragOverGapBottom,
      isLeaf,
      expanded,
      selected,
      checked,
      halfChecked,
      loading
    } = this.$props;
    const {
      vcTree: { prefixCls, filterTreeNode, draggable }
    } = this;
    const disabled = this.isDisabled();
    return h(
      'li',
      {
        class: {
          [`${prefixCls}-treenode-disabled`]: disabled,
          [`${prefixCls}-treenode-switcher-${expanded ? 'open' : 'close'}`]: !isLeaf,
          [`${prefixCls}-treenode-checkbox-checked`]: checked,
          [`${prefixCls}-treenode-checkbox-indeterminate`]: halfChecked,
          [`${prefixCls}-treenode-selected`]: selected,
          [`${prefixCls}-treenode-loading`]: loading,
          'drag-over': !disabled && dragOver,
          'drag-over-gap-top': !disabled && dragOverGapTop,
          'drag-over-gap-bottom': !disabled && dragOverGapBottom,
          'filter-node': filterTreeNode && filterTreeNode(this)
        },
        role: 'treeitem',
        onDragenter: draggable ? this.onDragEnter : noop,
        onDragover: draggable ? this.onDragOver : noop,
        onDragleave: draggable ? this.onDragLeave : noop,
        onDrop: draggable ? this.onDrop : noop,
        onDragend: draggable ? this.onDragEnd : noop
      },
      [
        this.renderSwitcher(h),
        this.renderCheckbox(h),
        this.renderSelector(h),
        this.renderChildren(h)
      ]
    );
  }
};

TreeNode.isTreeNode = 1;

export default TreeNode;
